Accessibility
Home / DevNet / Dreamweaver Development Center /
DevNet
Dreamweaver Article

Adding a Second Login Role for the Manager
Up to now this authentication discussion has mentioned only a single type of login for members who access restricted pages located in the "members" folder. Whenever you have members, however, you invariably need a super-user to manage all members' accounts. This is the manager account and it's the only user with unrestricted access to the pages in the "manager" folder, which allows administration of members' accounts. (The manager can also access member-only pages, of course.)

Forms authentication in ASP.NET supports multiple roles but requires additional programming:

  • You can no longer use the default FormsAuthenticationTicket created by RedirectFromLoginPage because you need to use the userData attribute of the ticket to store your role information and you don't want to have to use default.aspx as the default page for redirecting your visitors.
  • You need to discern the user's role information when they request restricted pages.

For the simpler authentication scheme discussed in the previous section, you looked at the web.config and login.aspx files. To support multiple roles you must edit the global.asax file.

First look at the changes needed in the web.config file.

Creating Multiple Roles in the web.config File
The site manager needs exclusive access to pages in the manager folder and to pages in the members folder; likewise, members must only have access to pages in the members folder. Table 1 shows the modifications required for the web.config file to support this more complex authentication scheme. Because you have two separate folders with restricted-access pages, you need two separate the <location> tags to define the folders' paths.

Look at the code snippet on the right side of Table 1. The path in the first <location> tag indicates that the following authorization rules apply to pages within the manager directory only. Upon login, the <authorization> tag allows any user in a manager role to access files and denies all other users (shown by the * in the code), even if the page has authenticated the user with any role other than the manager role.

The second location path specifies the members directory. The <authorization> tag allows access by any user in the member and/or manager roles and denies all other users (shown again by the *).

The code that you replace (left side of Table 1) applies to the unique case of a single type of login with no specific role assigned to it. The question mark in the deny users element means that all unauthenticated users are denied access.

Now let's look at how roles are assigned.

Creating the FormsAuthenticationTicket, Including Role Information
Remember the following check to authenticate a user's login credentials?

// authenticate user
if ((emailaddr.Value == DS_uidAndPwd.FieldValue("emailaddr",null)) &&
   (password.Value == DS_uidAndPwd.FieldValue("password",null))) 
{
   // The user has been authenticated. 
   FormsAuthentication.RedirectFromLoginPage(emailaddr.Value, false)
}
else 
{
   Msg.Text = "Invalid Credentials: Please try again";
}

This section discusses how to replace the call to RedirectFromLoginPage with code that constructs the ticket and then redirects it as appropriate. The RedirectFromLoginPage method, as you may recall, does more than just redirect away from the login page. It also creates FormsAuthenticationTicket. However, the ticket doesn't contain any role information.

Here's how to create and use your own ticket.

The constructor for FormsAuthenticationTicket has several forms. You're going to use this one. (The cookie created by RedirectFromLoginPage sets the version internally, the issue date to current date, and the expiration based on the cookie's life span.)

public FormsAuthenticationTicket(
   int version,
   string name,
   DateTime issueDate,
   DateTime expiration,
   bool isPersistent,
   string userData
);

The call to create the ticket looks like this:

FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
   1,             // version
   Request.Form["emailaddr"],   // get username/e-mail address from the form
   DateTime.Now,                // issue time is now
   DateTime.Now.AddMinutes(30), // expires in 30 minutes
   false,         // cookie is not persistent
   "member"       // role assignment is stored in userData
);

The userData string corresponds to the user's role information. In this example, the role is a single word but you could have multiple roles for each login in a comma-separated list. (See the discussion at the end for the Application_OnAuthenticateRequest method in Global.asax.)

Now you need to create a cookie with this ticket. Create a new (encrypted) HttpCookie using the ticket you just created and name it according to the value you specified in the <forms> tag in the web.config file:

 HttpCookie cookie = new HttpCookie( FormsAuthentication.FormsCookieName,
   FormsAuthentication.Encrypt(ticket) );

Add the cookie to the outbound response:

Response.Cookies.Add(cookie);

Creating the ticket and cookie and attaching the cookie to the outbound response are the first required tasks. You still need to redirect users upon their successful login. Once that's done, you've completely replaced the call to RedirectFromLoginPage. This method does a surprising amount of work, doesn't it?

Redirecting Upon Successful User Login
The final step in the login process is to redirect users as appropriate. Take your cue from the FormsAuthentication.RedirectFromLoginPage method. Here's what it does:

  • If the user originally requested a page from a protected directory, RedirectFromLoginPage redirects him or her back to that originally requested page.
  • If the user navigates to the login page directly, RedirectFromLoginPage redirects the user to default.aspx in the root folder.

The first redirect makes perfect sense: Redirect back to the originally requested page. The second redirect may confuse people: Instead of redirecting to default.aspx in the site's root folder, redirect to an index page inside the protected folder so that the logged-in member can now see all the great stuff he or she can access.

Upon successful login, redirect as appropriate:

String returnUrl;
if (Request.QueryString["ReturnURL"] == null)
{
   returnUrl = "/members/index.aspx";
}
else
{
   returnUrl = Request.QueryString["ReturnURL"];
}
Response.Redirect(returnUrl);

The URL of the originally requested file is stored in the ReturnURL key of the query string. This is part of the job of the ASP.NET framework. Say, for example, that an unlogged-in user navigates to the following page:

http://security/members/MemberOnlyFile.aspx

Because the user isn't logged in, the login page appears (note the query string that's automatically attached for you):

http://security/login.aspx?ReturnUrl=%2fmembers%2fMemberOnlyFile.aspx

So long as the user enters appropriate login credentials, the page redirects her or him automatically to the MemberOnlyFile.aspx file in the members folder.

Put it all together now. The code snippet from login.aspx in Listing 4 shows the code that replaces the call to FormsAuthentication.RedirectFromLoginPage.

The managerLogin.aspx Page
The whole reason for adding roles is so that you could establish a manager with its own login and set of protected pages. Look at managerLogin.aspx (in the root of the site), as annotated in Listing 5.

As you read through the code you'll notice similarities to the login.aspx page. One new concept is detecting whether the user is already logged in. The version of the login.aspx file that you've worked on so far doesn't exhibit this feature but probably should. (Also see "Determine if the user is already logged in" for more info.)

I've already discussed the changes necessary in the web.config file. (See the earlier section "Create multiple roles in the web.config file.") In addition to the manager's login page and modifications to web.config, role-based authentication requires event handling for OnAuthenticateRequest in Global.asax.

Handling the OnAuthenticateRequest Event in Global.asax
The OnAuthenticateRequest event is raised whenever a user navigates to a page in a folder that is protected by Forms authentication. Before you added the complexity of multiple roles this event was handled automatically for you by the ASP.NET framework. However, now you need to deal with it explicitly.

Global.asax, the ASP.NET application file, is an optional file that contains code for responding to application-level events raised by ASP.NET or by HTTP modules. You're going to add a section to handle the OnAuthenticateRequest event (see Listing 6).

When a page in the protected (members or manager) folder is requested, the ASP.NET framework automatically executes this method because the OnAuthenticateRequest event is raised. The first check is whether or not the user even exists. If the user does exist, is he or she authenticated (that is, does the user have a ticket)? If yes, get the ticket associated with this user and then get the userData value stored in the ticket and extract the list of roles. Finally, create a new GenericPrincipal using the current user's forms identity and roles.

 
 
Previous Contents Next